public/schema.org/<버전>/ 아래 파일들은 schema.org 가 배포하는 것이 아니다. 가공을 거친 것이고, 그 가공이 InterpreterSchema 와 SchemaOrg.scala 가 읽는 모양을 만든다. 버전을 올리려면 같은 가공을 다시 해야 한다.
파일 | 읽는 곳 | 쓰임 |
|---|---|---|
|
| class·property 정의 전부. |
| (읽지 않음) | 아래 pruned 를 만드는 재료로만 둔다 |
|
| class 계층 트리. |
5.0·14.0·26.0·30.1 넷이 있지만 애플리케이션이 읽는 것은 하나뿐이다. 어느 것인지는 SchemaOrg.scala 의 private val version 이 정한다 — 경로를 여러 곳에 적지 않는다. 2026-09-20 에 26.0 에서 30.1 로 올렸고, 나머지는 과거 것이다.
테스트와 스크립트는 그 상수를 읽는다. scripts/lib/schema-org.mjs 가 SchemaOrg.scala 에서 버전을 뽑아내므로, 한쪽만 올리고 다른 쪽을 잊어도 조용히 지나가지 않는다.
원본 둘은 서로 다른 곳에서 온다. 같은 릴리스 페이지에 있지 않다.
schemaorg-current-https.jsonld — https://github.com/schemaorg/schemaorg/blob/v<버전>/data/releases/<버전>/tree.jsonld — https://schema.org/docs/tree.jsonld. 릴리스에는 이 파일이 없다. 문서 경로가 내주는 현재 버전이므로, 릴리스 직후가 아니면 어휘와 버전이 어긋날 수 있다가공은 별도 저장소 SchemaOrgTransform 이 한다. original/<버전>/ 에 원본 둘을 넣고 node SchemaOrgTransform/index.js 를 돌리면 transformed/<버전>/ 에 셋이 나온다.
{"@id": "schema:Movie"} 처럼 키가 @id 하나뿐인 객체를 그 값으로 푼다@·rdf:·rdfs:·schema:·http://schema.org/ 접두어를 벗긴다tree.jsonld 에서 id·children 만 남겨 tree.pruned.jsonld 를 만든다그래서 배포본이 @graph·@id·@type·rdfs:Class·rdf:Property 라고 적는 자리를 우리 파일은 graph·id·type·Class·Property 라고 적는다. SchemaOrg.scala 는 그 가공된 모양을 (v \ "id").as[String] 로 바로 읽는다 — 가공 안 한 파일을 떨어뜨리면 Schema 블록이 있는 첫 페이지에서 JsResultException 이 난다.
test/schema-org-vocabulary.test.mjs 가 그 모양을 이름 붙여 고정한다. 가공을 빠뜨리면 런타임이 아니라 거기서 실패한다.
CalculatedSchemaOrg.isSchemaOrgTerm 이 두 가지를 버린다. 둘 다 26.0 에서는 아무것도 바꾸지 않고 27.0 부터 문다.
bibo:·cmns-*:·fibo-*:·gs1:·unece:·eli:. 30.1 기준 231개 이고 전부 Class 나 Property 로 잡혀서, 거르지 않으면 클래스 브라우저와 property 추천에 우리 것인 양 섞인다.comment 가 없는 것. 가공이 rdf:·rdfs: 접두어를 벗기는 것은 어쩔 수 없다 — class 가 자기를 class 라고 말하는 방법이 rdfs:Class 라서다. 그 부작용으로 rdf:type 과 rdfs:label 이 맨 type·label 로 도착해 schema.org property 처럼 보이고, 같은 이름의 키와 부딪힌다. 진짜 용어는 모두 rdfs:comment 를 갖고 이 둘만 아무것도 없다 — 그 차이로 가른다.원본 | 거른 뒤 | |
|---|---|---|
| 3256 | 3023 |
| 1016 | 939 |
| 1694 | 1538 |
용어를 버리고 그것을 가리키는 참조를 남기면 계층이 어긋난 채로 돈다. schema.org class 10개가 외부 class 를 부모로 선언한다 — Brand 는 cmns-cls:Classifier 의 하위이고 Country 는 cmns-ge:GeopoliticalEntity 의 하위다. 그대로 두면 getParents 가 어느 map 에도 없는 이름을 내주고 getPathHierarchy 가 그리로 걸어 들어간다.
그래서 keptOnly 가 subClassOf 와 domainIncludes 에서 namespace 붙은 것을 빼낸다. 10개 모두 schema.org 부모를 함께 갖고 있어서 고아가 되는 class 는 없다. domainIncludes 는 지금 그런 참조가 없지만 같은 규칙으로 덮어 뒀다 — 다음 릴리스가 조용히 하나 넣는 것을 막는다.
getClassHierarchy 는 원래 안전했다. mapClass.get 이 None 이면 Seq() 로 끝나서 외부 이름이 결과에 끼지 않는다. 문제는 그것을 거치지 않는 getParents 쪽이었다.
public/schema.org/custom.jsonld 가 우리가 정의하는 class 를 담는다. 버전 디렉터리 바깥에 있다 — 우리 것이고, 어휘를 올려도 바뀌지 않기 때문이다. 모양은 변환된 어휘와 같아서 parseGraph 가 둘 다 읽는다.
subClassOf 로 진짜 schema.org class 를 가리켜야 한다. 그것이 이 파일의 전부이자 값어치다 — 부모가 있어야 상속 property 추천이 생기고, jsonTree 에 접붙일 자리가 생기고, 그대로가 schema.org 제안서의 내용이 된다.
부모를 안 주면 mapClass 에 없는 class 와 똑같아진다. 그때도 렌더는 된다 — getSchemaClass 가 빈 SchemaType 으로 떨어지므로 — 다만 설명도 부모도 없고, 문서 목록에서 맨 아래 = Custom 평평한 더미로 간다.
class | 부모 | 쓰는 곳 | 제안할 만한가 |
|---|---|---|---|
|
| ISO 3166 계열 5개, ISO 8601, ISO 4217, BCP 47, RFC 2119 | 그렇다 |
|
| 꽃, 풀 | 그렇다 |
|
| The Macallan Sherry Oak 12 Years Old | 아니다 |
|
| Rémy Martin | 아니다 |
넷 다 새 property 가 하나도 필요 없다. 부모만 정해 주면 끝난다.
Standard — 쓰이는 것은 name·url·image·isPartOf·hasPart 뿐이고 전부 CreativeWork 상속분이다. isPartOf·hasPart 가 표준과 그 부분(ISO 3166 ↔ ISO 3166-1)의 관계를 그대로 담는다. Legislation 이 같은 모양의 선례다: CreativeWork 하위의 규범 문서 타입이고, 외부 공동체 어휘(ELI)에서 schema.org 로 들어왔다.
Poem — author·datePublished 뿐이고 둘 다 domain 이 CreativeWork 다. schema.org 에 이미 ShortStory 와 Play 가 있다 — CreativeWork 하위의 문학 형식 타입들이다. 그러니 시가 없는 것은 일부러 거칠게 둔 결과가 아니라 빈자리이고, 그것이 제안이 할 말 전부다.
Whiskey·Cognac — countryOfOrigin 의 domain 에 Product 가 있어서 Product 가 부모다. 제안 대상은 아니다: schema.org 에는 음료 제품 class 가 아예 없다 — Wine 도 Beer 도 FoodProduct 도 없고, Winery 같은 업소 타입과 Intangible 인 MenuItem 뿐이다. 빠진 것이 이름 하나가 아니라 가지 하나라서, 이름 하나를 제안하는 것은 앞뒤가 안 맞는다.
둘은 형제다. 코냑은 브랜디이지 위스키가 아니다. 중간 class(Spirit·AlcoholicBeverage)를 두지 않은 것은 페이지 두 개에 가지를 새로 만드는 일이라서다 — 세 번째 술이 생기면 그때 만든다.
JSON-LD 에는 정의한 이름 그대로 나간다. CreativeWork·Product + additionalType 으로 희석하지 않기로 했다(2026-09-20 소유자 결정) — 주장하려는 것이 «이것은 표준이다», «이것은 시다» 라서다.
jsonTree 는 어휘와 다른 파일이라 custom class 를 모른다. seqAll 에만 합치면 Standard 는 맵에서는 부모를 갖는데 정작 문서 목록에서는 = Custom 으로 떨어진다. 그래서 graftChild 가 트리를 읽으면서 부모 노드의 children 에 끼워 넣는다.
seqCustom 이 그 항목을 스스로 버린다. 진짜가 이기고, 우리 것은 쓰이지 않는다. 그리고 테스트가 그 충돌을 이름으로 말해 준다 — 그때가 custom.jsonld 에서 항목을 지우고, 제안을 냈다면 닫을 시점이다. 맵과 트리가 같은 seqCustom 하나를 읽으므로 한쪽에만 남는 일은 없다.
# 1. 원본 둘을 SchemaOrgTransform/original/<새버전>/ 에 놓는다 # 2. index.js 의 버전 목록에 <새버전> 을 더한다 ← 하드코딩이다 # 3. node index.js # 4. transformed/<새버전>/ 셋을 AhaWiki public/schema.org/<새버전>/ 로 옮긴다(줄끝 LF) # 5. SchemaOrg.scala 의 `private val version` 을 바꾼다 # 6. npm test 와 sbt test 가 개수를 다시 말해 준다
SchemaOrgTransform/index.js 의 prune 단계는 버전을 하드코딩한다. 목록에 없는 버전은 tree.pruned.jsonld 가 만들어지지 않고, 그 사실을 아무것도 말해 주지 않는다 — 앞 둘만 나오고 조용히 끝난다.
옮길 때 줄끝을 LF 로 맞춘다. SchemaOrgTransform/index.js 는 CRLF 로 쓰고 이 저장소는 LF 로 정규화한다. 2026-09-20 에 transformed/26.0 과 실려 있던 26.0 을 비교하니 바이트는 달랐고 파싱한 내용은 완전히 같았다 — 차이는 줄끝뿐이었다. 그 확인이 곧 이 파이프라인이 실린 파일을 재현한다는 증거다. 30.1 을 더하며 5.0·14.0·26.0 을 다시 돌려 봤고, 셋 다 그대로 재현됐다.
개수는 두 곳이 같이 단언한다 — test/schema-org-vocabulary.test.mjs 는 파일 쪽에서, SchemaOrgUnit.scala 는 적재된 map 쪽에서. 한쪽만 고치면 다른 쪽이 실패한다.
owner 가 생겼다 — domain=Thing, range=Organization·Person, "A person or organization who owns this Thing." 변환 표가 소유자를 일부러 비워 뒀던 이유가 «schema.org 에 없어서» 였고, 그 자리가 채워졌다. public/js/AhaWiki.WikipediaToSchema.js 의 테스트에 «생기면 다시 보라» 고 걸어 둔 줄이 어휘를 바꾸는 순간 실패해서 알았다. 지금은 Owner·Owners·소유자·소유주·소유기관 이 owner 로 간다.pronouns·legalAddress·jobDuration·referee·specification 등.tree.pruned.jsonld 에는 있는데 어휘에는 없는 이름이 StupidType 하나 있다. schema.org 자신의 시험용이고, 문서 트리에는 실리는데 어휘 어느 층에도 정의가 없다. 둘이 서로 다른 곳에서 오기 때문에 생기는 일이다. 테스트가 이 목록을 그대로 고정하므로, 달라지면 실패한다.
26.0 에서는 ProductReturnEnumeration·ProductReturnPolicy 도 같은 처지였다 — attic 으로 물러났는데 트리에 남아 있었다. 30.1 의 트리는 더 이상 그 둘을 부르지 않는다.
의료 전공명들(Dermatology·Nursing 등)은 다르다. 어휘에 type="MedicalSpecialty" 로 있고, 트리에 나오는 것이 맞다 — Class 가 아니라 열거형 값일 뿐이다.
Similar pages by cosine similarity. Words after page name are term frequency.